
/*关于按键：
遍历是一个简单的事，定义好parent，child, dn_sibble, up_sibble即可。
处理复杂的是叶子菜单项的定义（定义那些操作属于一个叶子菜单项，如定义了CAL_In）和处理。
工作量大，要编写每个叶子菜单项。
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>


#include "menu_seg_helper.h"
#include "parameter.h"
#include "drv_ch423s.h"

#include "ADCHal.h"
#include "weight_polyline.h"
#include "menu.h"
#include "menu_cal.h"
#include "key4.h"
#include "ble_disp_app.h"


extern cal_op_t cal_op;

extern uint8_t flag_para_update_adc;
extern uint8_t flag_para_update_dac;
extern uint8_t flag_para_update_com_plc;  // 通信参数。
extern uint8_t flag_para_update_fir;
extern uint8_t flag_ma_cal;

extern int32_t para_to_flash_2 (uint32_t);

#ifdef OFFSET_DIV_N_F
extern float offset_div_n_f_man;     // 手动触发
extern float offset_div_n_f_pwr_on;  // 上电自动清零
extern float offset_div_n_f_trace;   // 零点跟踪
extern float offset_div_n_f_tare;    // 去皮操作。
#else
extern int32_t offset_man;     // 手动归零的偏移量				在显示模式下，按下zero键；显示值在清零范围。			-1*显示值 --> offset
extern int32_t offset_pwr_on;  // 上电自动清零的偏移量			上电；上电清零开关有效；显示值在清零范围。				-1*显示值 --> offset_pwr_on
extern int32_t offset_trace;   // 零点跟踪的偏移量				显示值在跟踪范围；持续时间超过1.6秒。					-1*显示值 --> offset_zero_range
#endif







const MENU_ITEM menu_cal = {.id = 2,
                            .content = "   CAL",
                            .f_enter = NULL,
                            .f_key = key_Fn_trans,
                            .f_do_more = NULL,
                            .parent = NULL,
                            .child = (MENU_ITEM *)&menu_cal_pass,
                            .up_sibble = NULL,
                            .sibble = (MENU_ITEM *)&menu_setup};


/*CAL下子菜单*/
const MENU_ITEM menu_cal_pass = {.id = 12,
                                 .content = "  PASS",
                                 .f_enter = NULL,
                                 .f_key = leaf_Fn_cal_in,
                                 .f_do_more = NULL,
                                 .parent = (MENU_ITEM *)&menu_cal,
                                 .child = NULL,
                                 .up_sibble = NULL,
                                 .sibble = (MENU_ITEM *)&menu_cal_pt};

const MENU_ITEM menu_cal_pt = {.id = 22,
                               .content = "  Pt",
                               .para_name= "cal_pt",
                               .f_enter = NULL,
                               .f_key = leaf_Fn_select_idx,
                               .f_do_more = key_Fn_cal_save_and_update,
                               .tbl = (void *)_tbl_pt_pos,
                               .tbl_type = TBL_TYPE_STR,
                               .ppara_idx = &para_xxx.pt_pos_idx,
                               .parent = (MENU_ITEM *)&menu_cal,
                               .child = NULL,
                               .up_sibble = NULL,
                               .sibble = (MENU_ITEM *)&menu_cal_1d};

const MENU_ITEM menu_cal_1d = {.id = 32,
                               .content = "  1d",
                               .para_name= "cal_1d",
                               .f_enter = NULL,
                               .f_key = leaf_Fn_cal_1d,
                               .f_do_more = key_Fn_cal_save_and_update,
                               .parent = (MENU_ITEM *)&menu_cal,
                               .child = NULL,
                               .up_sibble = NULL,
                               .sibble = (MENU_ITEM *)&menu_cal_cp};

const MENU_ITEM menu_cal_cp = {.id = 42,
                               .content = "  CP",
                               .f_enter = NULL,
                               .f_key = leaf_Fn_cal_cp,
                               .f_do_more = key_Fn_cal_save_and_update,
                               .parent = (MENU_ITEM *)&menu_cal,
                               .child = NULL,
                               .up_sibble = NULL,
                               .sibble = (MENU_ITEM *)&menu_cal_t};

const MENU_ITEM menu_cal_t = {.id = 52,
                              .content = "   t",
                              .f_enter = NULL,
                              .f_key = key_Fn_trans,
                              .f_do_more = NULL,
                              .parent = (MENU_ITEM *)&menu_cal,
                              .child = (MENU_ITEM *)&menu_cal_t_mv,
                              .up_sibble = NULL,
                              .sibble = (MENU_ITEM *)&menu_cal_load1};

// 标定零点时显示毫伏值。
const MENU_ITEM menu_cal_t_mv = {.id = 152,
                                 .content = "   ---",
                                 .f_enter = NULL,
                                 .f_key = key_Fn_cal_t_mv,
                                 .f_do_more = NULL,
                                 .parent = (MENU_ITEM *)&menu_cal_t,
                                 .child = (MENU_ITEM *)&menu_cal_t_input_wt,
                                 .up_sibble = NULL,
                                 .sibble = (MENU_ITEM *)&menu_cal_t_input_mv};

// // 标定零点时显示固定毫伏值。
// const MENU_ITEM menu_cal_t_o_mv = {.id = 252,
//                                    .content = "   ---",
//                                    .f_enter = NULL,
//                                    .f_key = key_Fn_cal_t_o_mv,
//                                    .f_do_more = NULL,
//                                    .parent = (MENU_ITEM *)&menu_cal,
//                                    .child = (MENU_ITEM *)&menu_cal_t_input_wt,
//                                    .up_sibble = NULL,
//                                    .sibble = (MENU_ITEM *)&menu_cal_t_input_mv};

// 标定零点时手动输入毫伏值。
const MENU_ITEM menu_cal_t_input_mv = {.id = 352,
                                       .content = "   ---",
                                       .f_enter = NULL,
                                       .f_key = key_Fn_cal_t_input_mv,
                                       .f_do_more = NULL,
                                       .parent = (MENU_ITEM *)&menu_cal_t,
                                       .child = NULL,
                                       .up_sibble = NULL,
                                       .sibble = (MENU_ITEM *)&menu_cal_t_input_wt};


// 标定零点时手动输入称重值，先统一显示"   ---"，再实时显示。
const MENU_ITEM menu_cal_t_input_wt = {.id = 452,
                                       .content = "   ---",
                                       .f_enter = NULL,
                                       .f_key = key_Fn_cal_t_input_wt,
                                       .f_do_more = NULL,
                                       .parent = (MENU_ITEM *)&menu_cal_t,
                                       .child = NULL,
                                       .up_sibble = NULL,
                                       .sibble = (MENU_ITEM *)&menu_cal_load1};


const MENU_ITEM menu_cal_load1 = {.id = 62,
                                  .content = " Load1",
                                  .f_enter = NULL,
                                  .f_key = key_Fn_trans,
                                  .f_do_more = NULL,
                                  .parent = (MENU_ITEM *)&menu_cal,
                                  .child = (MENU_ITEM *)&menu_cal_load1_mv,
                                  .up_sibble = NULL,
                                  .sibble = (MENU_ITEM *)&menu_cal_pt};

// 加载点时显示毫伏值。
const MENU_ITEM menu_cal_load1_mv = {.id = 162,
                                 .content = "   ---",
                                 .f_enter = NULL,
                                 .f_key = key_Fn_cal_load1_mv,
                                 .f_do_more = NULL,
                                 .parent = (MENU_ITEM *)&menu_cal_load1,
                                 .child = (MENU_ITEM *)&menu_cal_load1_input_wt,
                                 .up_sibble = NULL,
                                 .sibble = (MENU_ITEM *)&menu_cal_load1_input_mv};


//标定加载点时手动输入称重值，先统一显示"   ---"，再实时显示。
const MENU_ITEM menu_cal_load1_input_wt = {.id = 1162,
                                       .content = "   ---",
                                       .f_enter = NULL,
                                       .f_key = key_Fn_cal_load1_input_wt,
                                       .f_do_more = NULL,
                                       .parent = (MENU_ITEM *)&menu_cal_load1,
                                       .child = NULL,
                                       .up_sibble = NULL,
                                       .sibble = NULL};

//标定加载点时手动输入毫伏值，先统一显示"   ---"，再实时显示。
const MENU_ITEM menu_cal_load1_input_mv = {.id = 262,
                                       .content = "   ---",
                                       .f_enter = NULL,
                                       .f_key = key_Fn_cal_load1_input_mv,
                                       .f_do_more = NULL,
                                       .parent = (MENU_ITEM *)&menu_cal_load1_mv,
                                       .child = NULL,
                                       .up_sibble = NULL,
                                       .sibble = (MENU_ITEM *)&menu_cal_load1_input_wt};                                       

/*动态显示（零点）毫伏值。
 */
int32_t key_Fn_cal_t_mv (void *p) {
    /*先动态显示毫伏值，再处理按键。
     */
    __disable_irq();                  // PFIC_DisableAllIRQ();	//PFIC_EnableAllIRQ();	//__disable_irq();
    cal_op.ad = ad_data.ad_filtered;  // 时刻更新标定操作用变量。
    __enable_irq();                   // PFIC_EnableAllIRQ();	//PFIC_DisableAllIRQ();	//__enable_irq();

    cal_op.uv = ad_to_uv (cal_op.ad);
    cal_op.wt = (int32_t)adc_to_div_n_f (cal_op.ad);

    // 显示当前毫伏值
    sprintf (ad_data.str, "%06d", cal_op.uv);  // 简单起见，暂不支持小数点。
    if (7 < strlen (ad_data.str))
        ad_data.str[8] = '\0';

    seg_updata(ad_data.str);            // 输入待显示的字符串（可以含小数点）和指示灯状态led_sta。更新了内部数据：.data_nc[]和.data[]。
    x_seg_set_point(SEG_L_3, 1);

    /*再处理按键：只支持ESC和Enter键。
    */
    if (0 == key_evt)
        return -1;  // 无按键。

    if (KEY_K1_ESC == key_evt) {
        // 返回上一级菜单。
        if (NULL != cur_item->parent) {
            cur_item = cur_item->parent;
            seg_updata (cur_item->content);
            
            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    if (KEY_K4_ENTER == key_evt) {
        // 进入“固定显示 o x.xxx”。
        if (NULL != cur_item->child) {
            cur_item = cur_item->child;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    
    if (KEY_K2_OPT == key_evt) {
        // 进入“固定显示 o x.xxx”。
        if (NULL != cur_item->sibble) {
            cur_item = cur_item->sibble;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }
    
    // 其他按键不处理，直接丢弃。
    key_evt = 0;
    return 0;
}



/*固定显示（零点）毫伏值。
 */
int32_t key_Fn_cal_t_o_mv (void *p) {
    /*显示之前的毫伏值：不再实时计算cal_op.uv。
     */
    sprintf (ad_data.str, "%6d", cal_op.uv);  // 简单起见，暂不支持小数点。
    ad_data.str[0] = 'o';

    if (7 < strlen (ad_data.str))
        ad_data.str[8] = '\0';

    x_disp_now (ad_data.str, 0, 0xFFFF);  // 输入待显示的字符串（可以含小数点）和指示灯状态led_sta。更新了内部数据：.data_nc[]和.data[]。


    /*再处理按键：支持ESC、Enter和OPT键。
     */
    if (0 == key_evt)
        return -1;  // 无按键。

    if (KEY_K1_ESC == key_evt) {
        // 返回上一级菜单。
        if (NULL != cur_item->parent) {
            cur_item = cur_item->parent;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    if (KEY_K4_ENTER == key_evt) {
        // 进入“输入称重值”菜单。
        if (NULL != cur_item->child) {
            cur_item = cur_item->child;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    if (KEY_K2_OPT == key_evt) {
        // 进入“手动输入mv值”菜单。
        if (cur_item->sibble) {
            cur_item = cur_item->sibble;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    // 其他按键不处理，直接丢弃。
    key_evt = 0;
    return 0;
}



/*输入（零点）毫伏值。
 */
int32_t key_Fn_cal_t_input_mv (void *p) {
	char str_i[16];
	
    /*初次执行时的配置，相当于执行key_Enter()或key_Exit()。
    */
    if (0 == sta_menu) {
        // 执行进入菜单的配置操作。
        //!!int_menu是全局变量。
        int_menu.dig = 6;
        int_menu.neg = 1;  // 允许负值。
        int_menu.val = 0;
        int_menu.max = 999999;
        int_menu.min = -99999;

        sprintf (str_i, "%06d", int_menu.val);
        seg_updata (str_i);  // 测试序列：PASS

        // 第1个‘0’闪
        int_menu.pos = SEG_L_1;
        x_seg_one_flash (int_menu.pos, DISP_X_MODE_FF);
        x_seg_set_point (PT_L_3, 1);


        sta_menu = 1;
    }


    /*再处理按键：支持ESC、Enter和OPT键。
     */
    if (0 == key_evt)
        return -1;  // 无按键。

    if (KEY_K1_ESC == key_evt) {
        // 返回上一级菜单。
        if (NULL != cur_item->parent) {
            cur_item = cur_item->parent;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }
    if (KEY_K4_ENTER == key_evt) {
        // 进入“输入称重值”菜单。
        if (NULL != cur_item->sibble) {
            //先将输入的毫伏值保存。
            cal_op.uv= int_menu.val;
            cal_op.ad= uv_to_ad(int_menu.val);  //计算AD值。

            sta_menu= 0;
            x_seg_flash_all(0);
            cur_item = cur_item->sibble;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    if (KEY_K2_OPT == key_evt) {
        int_menu_inc_one_seg (0);  // x_seg_inc_one(int_menu.pos);
        x_seg_set_point (PT_L_3, 1);
    } else if (KEY_K3_MODE == key_evt) {
        int_menu_change_pos();
        x_seg_set_point (PT_L_3, 1);
    } 

    // 其他按键不处理，直接丢弃。
    key_evt = 0;
    return 0;
}



/*输入（零点）毫伏值。
 */
int32_t key_Fn_cal_t_input_wt (void *p) {
	char str_i[16];
    char tmp6[16];
	
    /*初次执行时的配置，相当于执行key_Enter()或key_Exit()。
    */
    if (0 == sta_menu) {
        //根据当前小数点，
        if(0 == para_xxx.pt_pos_idx){
            seg_updata("0");    
        }
        else if(1 == para_xxx.pt_pos_idx){
            seg_updata("0.0");    
        }
        else if(2 == para_xxx.pt_pos_idx){
            seg_updata("0.00");    
        }
        else if(3 == para_xxx.pt_pos_idx){
            seg_updata("0.000");    
        }
        else if(4 == para_xxx.pt_pos_idx){
            seg_updata("0.0000");    
        }
        else{
            PRINT("err:pt\n");
        }

        sta_menu = 1;
    }


    if(0 == key_evt)
        return  -1;


    //处理按键：
    //输入密码：按opt键调值，按mode键移位，按Enter键进行密码确认，按Esc键无效。
    if (KEY_K2_OPT == key_evt) {
        //int_menu_inc_one_seg (0);  // x_seg_inc_one(int_menu.pos);
    } else if (KEY_K3_MODE == key_evt) {
        //int_menu_change_pos();
    } else if (KEY_K4_ENTER == key_evt) {
        //执行零点标定（毫伏值来自QQ，称重值为0）：
        PRINT("do cal t,uv=%d,ad=%d\n",cal_op.uv, cal_op.ad);
        CAL_CTX cal_ctx_bk;

        cal_save_to_pt (&cal_ctx_bk);

        cal_ctx.pt[0].ad = cal_op.ad;
        cal_ctx.pt[0].load = 0;
        cal_ctx.pt[0].cap_cal = fs_max;           // 保存表示是的量程。量程可变。
        cal_ctx.pt[0].div_cal = para_xxx.fd_min;  // 保存标定时的分度值，无屌用。
        cal_ctx.pt[0].k = 9.12345678F;            // 固定为9.12345678F，看看float表达为什么，输出显示“9.123457”。
        cal_ctx.pt[0].uv = cal_op.uv;

        // （重新）标定零点对加载点的影响。只简单平移ad，不重新计算load。
        // TODO需要知道ad的变化量=new-old= pt[0].ad- pt.ad。
        cal_update_ad (cal_ctx.pt[0].ad - cal_ctx_bk.pt[0].ad);  // 更新所有加载点对应的ad值。

        if(0 == para_cal_to_flash(0)){
            PRINT("OK:t\n");

            if(NULL != cur_item->sibble){
                cur_item= cur_item->sibble;
                seg_updata(cur_item->content);

                sta_menu= 0;
            }
        }

    } else if(KEY_K1_ESC == key_evt) {
        //返回上级菜单。
        if(NULL != cur_item->parent){
            cur_item= cur_item->parent;
            seg_updata(cur_item->content);

            sta_menu= 0;
        }
    }

    key_evt= 0;
    return 0;
}




/*动态显示（加载点1）毫伏值。
 */
int32_t key_Fn_cal_load1_mv (void *p) {
    /*先动态显示毫伏值，再处理按键。
     */
    __disable_irq();                  // PFIC_DisableAllIRQ();	//PFIC_EnableAllIRQ();	//__disable_irq();
    cal_op.ad = ad_data.ad_filtered;  // 时刻更新标定操作用变量。
    __enable_irq();                   // PFIC_EnableAllIRQ();	//PFIC_DisableAllIRQ();	//__enable_irq();

    cal_op.uv = ad_to_uv (cal_op.ad);
    cal_op.wt = (int32_t)adc_to_div_n_f (cal_op.ad);

    // 显示当前毫伏值
    sprintf (ad_data.str, "%6d", cal_op.uv);  // 简单起见，暂不支持小数点。
    if (7 < strlen (ad_data.str))
        ad_data.str[8] = '\0';

    x_disp_now (ad_data.str, 0, 0xFFFF);  // 输入待显示的字符串（可以含小数点）和指示灯状态led_sta。更新了内部数据：.data_nc[]和.data[]。


    /*再处理按键：只支持ESC和Enter键。
    */
    if (0 == key_evt)
        return -1;  // 无按键。

    if (KEY_K1_ESC == key_evt) {
        // 返回上一级菜单。
        if (NULL != cur_item->parent) {
            cur_item = cur_item->parent;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    if (KEY_K4_ENTER == key_evt) {
        if (NULL != cur_item->child) {           
            cur_item = cur_item->child;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    if (KEY_K2_OPT == key_evt) {
        if (NULL != cur_item->sibble) {
            cur_item = cur_item->sibble;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    // 其他按键不处理，直接丢弃。
    key_evt = 0;
    return 0;
}


/*动态显示（加载点1）毫伏值。
 */
int32_t key_Fn_cal_load1_input_mv (void *p) {
 	char str_i[16];
	
    /*初次执行时的配置，相当于执行key_Enter()或key_Exit()。
    */
    if (0 == sta_menu) {
        // 执行进入菜单的配置操作。
        //!!int_menu是全局变量。
        int_menu.dig = 6;
        int_menu.neg = 1;  // 允许负值。
        int_menu.val = 0;
        int_menu.max = 999999;
        int_menu.min = -99999;

        sprintf (str_i, "%06d", int_menu.val);
        seg_updata (str_i);  // 测试序列：PASS

        // 第1个‘0’闪
        int_menu.pos = SEG_L_1;
        x_seg_one_flash (int_menu.pos, DISP_X_MODE_FF);
        x_seg_set_point (PT_L_3, 1);


        sta_menu = 1;
    }


    /*再处理按键：支持ESC、Enter和OPT键。
    */
    if (0 == key_evt)
        return -1;  // 无按键。

    if (KEY_K1_ESC == key_evt) {
        // 返回上一级菜单。
        if (NULL != cur_item->parent) {
            cur_item = cur_item->parent;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }
    if (KEY_K4_ENTER == key_evt) {
        // 进入“输入称重值”菜单。
        if (NULL != cur_item->sibble) {
            //先将输入的毫伏值保存。
            cal_op.uv= int_menu.val;
            cal_op.ad= uv_to_ad(int_menu.val);  //计算AD值，因为AD在称重中起作用。

            x_seg_flash_all(0);
            cur_item = cur_item->sibble;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        key_evt = 0;
        return 0;
    }

    if (KEY_K2_OPT == key_evt) {
        int_menu_inc_one_seg (0);  // x_seg_inc_one(int_menu.pos);
        x_seg_set_point (PT_L_3, 1);
    } else if (KEY_K3_MODE == key_evt) {
        int_menu_change_pos();
        x_seg_set_point (PT_L_3, 1);
    } 

    // 其他按键不处理，直接丢弃。
    key_evt = 0;
    return 0;
}



/*输入（加载点）毫伏值。
 */
int32_t key_Fn_cal_load1_input_wt (void *p) {
	char str_i[16];
    char tmp6[16];
	
    //PRINT("sta_menu= %d\n", sta_menu);
    /*初次执行时的配置，相当于执行key_Enter()或key_Exit()。
    */
    if (0 == sta_menu) {
        //!!int_menu是全局变量。
        int_menu.dig = 6;
        int_menu.neg = 0;
        int_menu.val = 0;
        int_menu.max = 999999;
        int_menu.min = 0;

        sprintf (str_i, "%06d", int_menu.val);
        seg_updata (str_i);  // 测试序列：PASS

        // 第1个‘0’闪
        int_menu.pos = SEG_L_1;
        x_seg_one_flash (int_menu.pos, DISP_X_MODE_FF);

        sta_menu= 1;
    }


    if(0 == key_evt)
        return  -1;


    //处理按键：
    //输入密码：按opt键调值，按mode键移位，按Enter键进行密码确认，按Esc键无效。
    if (KEY_K2_OPT == key_evt) {
        int_menu_inc_one_seg (0);  // x_seg_inc_one(int_menu.pos);
    } else if (KEY_K3_MODE == key_evt) {
        int_menu_change_pos();
    } else if (KEY_K4_ENTER == key_evt) {
        sprintf (tmp6, "%06d", int_menu.val);

        cal_op.load= int_menu.val;

        //执行零点标定（毫伏值来自QQ，称重值为0）：
        PRINT("cal Load ,ad=%d,uv=%d,L=%d\n",cal_op.uv, cal_op.ad, cal_op.load);
        CAL_CTX cal_ctx_bk;

        cal_save_to_pt (&cal_ctx_bk);

        cal_op.cal_n= 2;    //加载点1。!!零点时为1。
        cal_ctx.pt[cal_op.cal_n - 1].uv = cal_op.uv;  // 当前实时上报的uv,ad。
        cal_ctx.pt[cal_op.cal_n - 1].ad = cal_op.ad;
        cal_ctx.pt[cal_op.cal_n - 1].used = 1;
        cal_ctx.pt[cal_op.cal_n - 1].load = cal_op.load;
        cal_ctx.pt[cal_op.cal_n - 1].cap_cal = fs_max;                                                                                          // 保存标定时的量程。量程可变，但此值不变。
        cal_ctx.pt[cal_op.cal_n - 1].div_cal = para_xxx.fd_min;                                                                                 // 标定时的分度值。分度值可变，但此值不变。
        cal_ctx.pt[cal_op.cal_n - 1].load_div_n_f = (int32_t)(cal_ctx.pt[cal_op.cal_n - 1].load / cal_ctx.pt[cal_op.cal_n - 1].div_cal + 0.5);  //.load_div_n_f为float。

        cal_ctx.pt[cal_op.cal_n - 1].k = (cal_ctx.pt[cal_op.cal_n - 1].ad - cal_ctx.pt[cal_op.cal_n - 2].ad);
        cal_ctx.pt[cal_op.cal_n - 1].k /= (float)(cal_ctx.pt[cal_op.cal_n - 1].load - cal_ctx.pt[cal_op.cal_n - 2].load);  //!!!必须先强制转换为float，否则值由2601.699951变为2601.000000。

        // 将本加载点的值拷贝到之后的所有加载点。
        cal_update_other_load (cal_op.cal_n - 1);

        if(0 == para_cal_to_flash(0)){
            PRINT("OK:Load1\n");

            if(NULL != cur_item->parent){
                cur_item= cur_item->parent;
                seg_updata(cur_item->content);
                x_seg_flash_all(0);

                sta_menu= 0;
            }
        }

    } else if(KEY_K1_ESC == key_evt) {
        //返回上级菜单。
        sta_menu= 0;

        if(NULL != cur_item->parent){
            cur_item= cur_item->parent;
            seg_updata(cur_item->content);
            x_seg_flash_all(0);

            sta_menu= 0;
        }
    }

    key_evt= 0;
    return 0;
}



/*进入校准流程的叶子菜单项处理函数。即完成密码验证。
 */
int32_t leaf_Fn_cal_in (void *p) {
    static uint8_t sta = 0;
    static uint8_t idx_seg = 1;       // 有效范围：1~6
    static uint8_t cnt_pass_err = 0;  // 密码错误次数，但4次时锁定（只能关电）。
    static char tmp6[8];              // 有效范围：1~6.
    uint16_t key;
    char str_i[7];

    if (0 == sta_menu) {
        //!!int_menu是全局变量。
        int_menu.dig = 6;
        int_menu.neg = 0;
        int_menu.val = 0;
        int_menu.max = 999999;
        int_menu.min = 0;

        sprintf (str_i, "%06d", int_menu.val);
        seg_updata (str_i);  // 测试序列：PASS

        // 第1个‘0’闪
        int_menu.pos = SEG_L_1;
        x_seg_one_flash (int_menu.pos, DISP_X_MODE_FF);

        PRINT("88\n");
        sta_menu= 1;
    }

    if(0 == key_evt)
        return  -1;


    //处理按键：
    //输入密码：按opt键调值，按mode键移位，按Enter键进行密码确认，按Esc键无效。
    if (KEY_K2_OPT == key_evt) {
        int_menu_inc_one_seg (0);  // x_seg_inc_one(int_menu.pos);
    } else if (KEY_K3_MODE == key_evt) {
        int_menu_change_pos();
    } else if (KEY_K4_ENTER == key_evt) {
        PRINT("99\n");
        sprintf (tmp6, "%06d", int_menu.val);

        // 比较密码，如果正确，进入下一个邻居菜单。
        if (0UL == pass_compare (tmp6)) {
            sta_menu = 0;

            cur_item = cur_item->sibble;
            seg_updata (cur_item->content);                  // cur_item->f_enter();
            
            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。

            x_seg_one_flash (int_menu.pos, DISP_X_MODE_ON);  // 关闭闪烁
        } else {
            //密码错误：什么都不做。
        }
    } else {
        // 不处理
    }

    key_evt= 0;
    return 0;
}





int32_t key_Fn_cal_save_and_update(void* p){
    para_xxx_to_flash(0);
    update_para_2nd();

    return 0;
}

/*小数点位置叶子菜单项处理函数。
带有状态的（sta）。
从pt开始，经1d,cp,t,load1器件不能退出，之后的load2,load3,load4,sen,siocal和pass可以退出。
返回值：
-1：无按键
0 : 按键已处理
>0: 特殊意思
*/
int32_t leaf_Fn_cal_pt (void *p) {
    static uint8_t sta = 0;
    //	static uint8_t idx_seg= 1;	//有效范围：1~6
    static uint8_t pos;  // 小数点位置。
    char tmp8[8];
    uint16_t key;

    // if (0 == key_evt)
    //     return -1;

    switch (sta_menu) {
    case 0:
        if (key_evt == KEY_K4_ENTER) {
            // 读取当前小数点，并显示。
            pos = para_xxx.pt_pos_idx;

            show_pt (pos, tmp8);
            seg_updata (tmp8);

            x_seg_set_point (6 - pos, 1);  // pos_dot为0：个位小数点（也显示出来）；为1：十位小数点...

            sta_menu = 1;
        } else if (key_evt == KEY_K3_MODE) {
            // 在同级菜单循环切换。
            cur_item = cur_item->sibble;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        } else if (key_evt == KEY_K1_ESC) {
            // 返回上一级菜单。
            cur_item = cur_item->parent;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        break;

    case 1:
        // 按OPT键调整数值，按Enter确认并进入同级下一个菜单。
        if (key_evt == KEY_K2_OPT) {
            pos++;
            pos %= 5;  // 0,1,2,3,4.

            // 重新显示小数点位置。
            show_pt (pos, tmp8);
            seg_updata (tmp8);

            x_seg_set_point (6 - pos, 1);
        } else if (key_evt == KEY_K3_MODE) {
            if (0 == pos)
                pos = 4;
            else
                pos--;

            pos %= 5;  // 0,1,2,3,4.

            // 重新显示小数点位置。
            show_pt (pos, tmp8);
            seg_updata (tmp8);

            x_seg_set_point (6 - pos, 1);
        } else if (key_evt == KEY_K4_ENTER) {
            // 保存旧值。
            uint16_t val_tmp16 = para_xxx.pt_pos_idx;
            para_xxx.pt_pos_idx = pos;


            // TODO保存
            if (0 == para_cal_to_flash (0)) {
                x_seg_flash_all(0);     //不再闪显。
                PRINT ("OK:cal>>FLASH\n");
            } else {
                PRINT ("err:cal>>FLASH\n");
            }
        }

        break;

    default:
        break;
    }
}

/*分度值叶子菜单项处理函数。1d不是Ld。
分度值：1/2/5/10/20/50。索引：0，1，2，3，4，5。有些麻烦啊，索引---真实值
*/
int32_t leaf_Fn_cal_1d (void *p) {
    static uint8_t sta = 0;
    static uint16_t val_change = 1;  // 有效范围：[0, 5] -> 1/2/5/10/20/50.
    char tmp8[8];
    uint16_t key;

    if (0 == key_evt)
        return -1;


    switch (sta_menu) {
    case 0:
        if (key_evt == KEY_K4_ENTER) {
            // 读取现有分度值并闪显。
            val_change = 0;  // 默认值（若未在_tbl_int_fd[]中找到，也是这个值）
            for (int i = 0; i < 6; i++) {
                if (para_xxx.fd_min == _tbl_int_fd[i]) {
                    val_change = i;
                    break;
                }
            }

            show_1d (val_change, tmp8);
            seg_updata (tmp8);

            x_seg_one_flash (5, DISP_X_MODE_FF); //set_one_seg_flash (5, SEG_FLASH);
            x_seg_one_flash (6, DISP_X_MODE_FF); //set_one_seg_flash (6, SEG_FLASH);

            sta_menu = 1;
        } else if (key_evt == KEY_K3_MODE) {
            // 在同级菜单循环切换。
            cur_item = cur_item->sibble;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        } else if (key_evt == KEY_K1_ESC) {
            //uartS ("\nesc ld");

            cur_item = cur_item->parent;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        break;

    case 1:
        // 按OPT选择分度值，按Enter键设置，并进入下一个同级菜单。
        if (key_evt == KEY_K2_OPT) {
            // 调整分度值。
            if (0 == val_change)
                val_change = 5;
            else
                val_change--;

            show_1d (val_change, tmp8);
            seg_updata (tmp8);
        } else if (key_evt == KEY_K3_MODE) {
            // 调整分度值。
            if (5 <= val_change)
                val_change = 0;
            else
                val_change++;

            show_1d (val_change, tmp8);
            seg_updata (tmp8);
        } else if (key_evt == KEY_K4_ENTER) {
            // 保存旧值。
            uint16_t val_tmp16 = para_xxx.fd_min;
            para_xxx.fd_min = _tbl_int_fd[val_change];


            // 保存参数到FLASH。
            if (cur_item->f_do_more) {
                if (0 == cur_item->f_do_more (0)) {
                    PRINT ("OK:%s to FLASH\n", cur_item->para_name);

                    x_seg_flash_all (0);

                    cur_item = cur_item->sibble;
                    seg_updata (cur_item->content);

                    sta_menu = 0;
                    x_seg_flash_all (0);  // seg_flash_all(0);
                } else {
                    PRINT ("err:save to FLASH\n");
                }
            }
        } else if (key_evt == KEY_K1_ESC) {
            // 不保存当前分度值，在同级菜单循环切换？
            seg_updata (cur_item->content);

            sta_menu = 0;
            x_seg_flash_all(0); //seg_flash_all (0);
        }
        break;

    default:
        break;
    }

    key_evt= 0;
    return 0;
}



/*最大量程叶子菜单项处理函数。
!!按当前的pt值显示。
*/
int32_t leaf_Fn_cal_cp (void *p) {
    static uint8_t sta = 0;
    static uint8_t idx_seg = 1;  // 有效范围：1~6
    char tmp8[8];                //
    char str_i[7];
    uint16_t key;

    if (0 == key_evt)
        return -1;


    switch (sta) {
    case 0:
        if (key_evt == KEY_K4_ENTER) {
            // 进入最大量程设置，并显示“100000”。!!不读取当前量程。
            if (fs_max > 999999)
                fs_max = 0;
            if (fs_max < -99999)
                fs_max = 0;

            //!!int_menu是全局变量。
            int_menu.dig = 6;
            int_menu.neg = 0;
            int_menu.val = fs_max;
            int_menu.max = 999999;
            int_menu.min = 0;

            sta = 1;
            sprintf (str_i, "%06d", int_menu.val);
            seg_updata (str_i);  // 测试序列：PASS

            // 第1个‘0’闪
            int_menu.pos = SEG_L_1;
            x_seg_one_flash (int_menu.pos, DISP_X_MODE_FF);
            x_seg_set_point (5 - para_xxx.pt_pos_idx, 1);  // 显示小数点

            sta = 1;
        } else if (key_evt == KEY_K3_MODE) {
            // 在同级菜单循环切换。
            cur_item = cur_item->sibble;
            seg_updata (cur_item->content);

            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        } else if (key_evt == KEY_K1_ESC) {
            // 返回上一级菜单。
            cur_item = cur_item->parent;
            seg_updata (cur_item->content);
            
            sta_menu= 0;    //切换菜单时，须写上sta_menu=0。
        }

        break;

    case 1:
        // 按OPT键调整数值，按MODE键移位，按Enter确认并进入同级下一个菜单。
        if (key_evt == KEY_K2_OPT) {
            int_menu_inc_one_seg (0);  // x_seg_inc_one(int_menu.pos);
            x_seg_set_point (5 - para_xxx.pt_pos_idx, 1);
        } else if (key_evt == KEY_K3_MODE) {
            int_menu_change_pos();
        } else if (key_evt == KEY_K4_ENTER) {
            // 保存旧值。
            uint16_t val_tmp16 = para_xxx.fs_max_hi;
            uint16_t val_tmp16_2 = para_xxx.fs_max_lo;

            x_seg_flash_all (0);  // 关闭闪烁

            para_xxx.fs_max_hi = int_menu.val >> 16;
            para_xxx.fs_max_lo = (uint16_t)int_menu.val;


            // 保存参数到FLASH。
            if (cur_item->f_do_more) {
                if (0 == cur_item->f_do_more (0)) {
                    PRINT ("OK:%s to FLASH\n", cur_item->para_name);

                    x_seg_flash_all (0);

                    cur_item = cur_item->sibble;
                    seg_updata (cur_item->content);

                    sta_menu = 0;
                    x_seg_flash_all (0);  // seg_flash_all(0);
                } else {
                    PRINT ("err%s to FLASH\n", cur_item->para_name);
                }
            }

            
            // 保存参数到FLASH。
            if (cur_item->f_do_more) {
                if (0 == cur_item->f_do_more (0)) {
                    PRINT ("OK:%s to FLASH\n", cur_item->para_name);

                    x_seg_flash_all (0);

                    cur_item = cur_item->sibble;
                    seg_updata (cur_item->content);

                    sta_menu = 0;
                    x_seg_flash_all (0);  // seg_flash_all(0);
                } else {
                    PRINT ("err:save to FLASH\n");
                }
            }
        } else if (key_evt == KEY_K1_ESC) {
            sta = 0;

            seg_updata (cur_item->content);
            x_seg_flash_all (0);
            x_seg_set_point (5 - para_xxx.pt_pos_idx, 0);
        }

        break;

    default:
        break;
    }
}

/*标定零点叶子菜单项处理函数。
!!显示当前的mv值。（第1个数码管显示t，不可被覆盖）。
在显示mv值时，如果STAB指示灯常亮，按Enter键保存当前零点值，并进入下一个菜单项。
从pt开始，经1d,cp,t,load1器件不能退出，之后的load2,load3,load4,sen,siocal和pass可以退出。
之前使用状态机，现在使用多级子菜单。
*/
int32_t leaf_Fn_cal_t (void *p) {
    static uint8_t sta = 0;
    static uint8_t disp_mv = 0;       // 是否显示mv值。
    static int32_t ad;                // 和uV伴随而来的ad值。
    static int32_t uv;                // 和ad伴随而来的uv值。
    static int32_t uv_man = -999999;  // 默认为-999999，不是手动输入mv值。
    uint16_t key;
    char tmp8[8];
    char str_i[7];
    char str[32];


    if(0 == key_evt)
        return -1;

    if (key_evt == KEY_K4_ENTER) {
        sta_menu= 0;    //复位状态。

        if(cur_item->child){
            cur_item= cur_item->child;
            seg_updata(cur_item->content);
        }
    }

    key_evt= 0;
    return 0; 
}


/*标定第n(0<n)点叶子菜单项处理函数。
先显示LoAdn，确定后显示"Add Ld"约1秒后，显示mv值。再确定，如果此时STAB有效，则进入设置标定1的称重值；无效（不稳）则死等。QQ如何退出死等状态？
在显示mv值时，如果STAB指示灯常亮，按Enter键保存当前零点值，并进入下一个菜单项。
*/
void leaf_Fn_cal_load_n (void* p) {
    // static uint8_t sta = 0;
    // static uint8_t disp_mv = 0;       // 是否显示mv值。
    // static int32_t ad;                // 和uV伴随而来的ad值。
    // static int32_t uv;                // 和ad伴随而来的uv值。
    // static int32_t uv_man = -999999;  // 默认为-999999，不是手动输入mv值。
    // uint16_t key;
    // char tmp8[8];
    // char str_i[7];
    // char str[32];

    // if (msg.type == MSG_TYPE_KEY) {
    //     key = msg.key;

    //     switch (sta) {
    //     case 0:  // 初始：设置滤波标志，设置disp_mv。
    //         if (KEY_K4_ENTER == key) {
    //             seg_updata ("Add Ld");
    //             osDelay (1000);

    //             disp_mv = 1;
    //             mode_mv_wt = MODE_MV;

    //             sta = 1;

    //             // 准备调整校准过程中的滤波强度。
    //             flag_para_update_fir = 'C';
    //         } else if (KEY_K3_MODE == key) {
    //             // 在同级菜单循环切换。
    //             cur_item = cur_item->sibble;
    //             seg_updata (cur_item->content);
    //         } else if (KEY_K1_ESC == key) {
    //             // 返回上一级菜单。
    //             cur_item = cur_item->parent;
    //             seg_updata (cur_item->content);

    //             x_seg_set_point (SEG_L_3, 0);
    //         }

    //         break;

    //     case 1:  // 动态显示当前mv值。
    //         if (key == KEY_K1_ESC) {
    //             sta = 0;
    //             disp_mv = 0;

    //             seg_updata (cur_item->content);
    //             x_seg_set_point (5 - para_xxx.pt_pos_idx, 0);


    //             // 滤波强度要恢复。
    //             flag_para_update_fir = 'X';
    //         } else if (key == KEY_K2_OPT) {
    //             //
    //             //!!int_menu是全局变量。
    //             int_menu.dig = 6;
    //             int_menu.neg = 1;  // 允许负值。
    //             int_menu.val = 0;
    //             int_menu.max = 999999;
    //             int_menu.min = -99999;

    //             sta = 1;
    //             sprintf (str_i, "%06d", int_menu.val);
    //             seg_updata (str_i);

    //             // idx_flash= SEG_L_1;
    //             x_seg_flash_all (0);
    //             int_menu.pos = SEG_L_1;
    //             x_seg_one_flash (int_menu.pos, DISP_X_MODE_FF);
    //             x_seg_set_point (PT_L_3, 1);

    //             sta = 3;
    //             disp_mv = 0;  // 不再使用底层送上来的mv值。
    //         } else if (key == KEY_K4_ENTER) {

    //             // 需要判断当前STAB为1才能保存，否则什么也不做。
    //             if (stab_arr.mv_stab) {
    //                 disp_mv = 0;  // 停止动态显示

    //                 // 保存当前mv值。
    //                 cal_ctx.pt[cur_item->id].uv = uv;

    //                 //
    //                 //!!int_menu是全局变量。
    //                 int_menu.dig = 6;
    //                 int_menu.neg = 0;
    //                 int_menu.val = 0;

    //                 // sta= 1;
    //                 sprintf (str_i, "%06d", int_menu.val);
    //                 seg_updata (str_i);  // 测试序列：PASS

    //                 int_menu.pos = SEG_L_1;
    //                 x_seg_one_flash (int_menu.pos, DISP_X_MODE_FF);
    //                 x_seg_set_point (5 - para_xxx.pt_pos_idx, 1);

    //                 sta = 2;
    //             }
    //         }

    //         break;

    //     case 2:  // 手动输入称重值，并计算k后保存。
    //         // 按opt键调值，按mode键移位，按Enter键进行密码确认，按Esc键无效。
    //         if (key == KEY_K2_OPT) {
    //             int_menu_inc_one_seg (0);  // x_seg_inc_one(int_menu.pos);
    //             x_seg_set_point (5 - para_xxx.pt_pos_idx, 1);
    //         } else if (key == KEY_K3_MODE) {
    //             int_menu_change_pos();
    //             x_seg_set_point (5 - para_xxx.pt_pos_idx, 1);
    //         } else if (key == KEY_K4_ENTER) {
    //             x_seg_flash_all (0);  // 关闭闪烁

    //             // 保存旧值。
    //             if (4 < cur_item->id) {
    //                 sprintf (str, "\n$err load %d", cur_item->id);
    //                 uartS (str);
    //                 break;
    //             }

    //             CAL_CTX cal_ctx_bk;

    //             cal_save_to_pt (&cal_ctx_bk);

    //             /*计算k
    //              */
    //             cal_ctx.pt[cur_item->id].uv = uv;  // 当前实时上报的uv,ad。
    //             cal_ctx.pt[cur_item->id].ad = ad;
    //             cal_ctx.pt[cur_item->id].used = 1;
    //             cal_ctx.pt[cur_item->id].load = int_menu.val;
    //             cal_ctx.pt[cur_item->id].cap_cal = fs_max;                                                                             // 保存标定时的量程。量程可变，但此值不变。
    //             cal_ctx.pt[cur_item->id].div_cal = para_xxx.fd_min;                                                                    // 标定时的分度值。分度值可变，但此值不变。
    //             cal_ctx.pt[cur_item->id].div_cnt = (int32_t)(cal_ctx.pt[cur_item->id].load / cal_ctx.pt[cur_item->id].div_cal + 0.5);  //.div_cnt是一个整数。TODO
    //             /*TODO上式为什么+0.5？
    //             A:因为.div_cal应该是1，2，5或10，20，50等。
    //               可能原因是实测后更改的。
    //             */


    //             if (-999999 != uv_man) {
    //                 // 使用手动输入值
    //                 cal_ctx.pt[cur_item->id].uv = uv_man;  // 相对mv值。
    //                 cal_ctx.pt[cur_item->id].ad = mv_to_ad (uv_man + cal_ctx.pt[0].uv);
    //                 cal_ctx.pt[cur_item->id].used = 1;

    //                 sprintf (str, "\n\nLoad%d manual", cur_item->id);
    //                 uartS (str);
    //             }

    //             if (0 == (cal_ctx.pt[cur_item->id].load - cal_ctx.pt[cur_item->id - 1].load)) {
    //                 cal_restore_from_pt (&cal_ctx_bk);

    //                 // 直接报错退出。
    //                 seg_updata ("Err  0");

    //                 sprintf (str, "\n$err save load %d", cur_item->id);
    //                 uartS (str);
    //                 osDelay (1000);

    //                 return;
    //             }

    //             cal_ctx.pt[cur_item->id].k = (cal_ctx.pt[cur_item->id].ad - cal_ctx.pt[cur_item->id - 1].ad);
    //             cal_ctx.pt[cur_item->id].k /= (float)(cal_ctx.pt[cur_item->id].load - cal_ctx.pt[cur_item->id - 1].load);  //!!!必须先强制转换为float，否则值由2601.699951变为2601.000000。

    //             // 将本加载点的值拷贝到之后的所有加载点。
    //             cal_update_other_load (cur_item->id);


    //             if (0 == para_to_flash_2 (FLASH_UPDATE_MASK_PARA_CAL)) {
    //                 char str[128];
    //                 sprintf (str, "\n$ok save load%d= %d, ad=%d, cap_cal=%d,div_cal=%f, k=%f", cur_item->id, cal_ctx.pt[cur_item->id].load, cal_ctx.pt[cur_item->id].ad, cal_ctx.pt[cur_item->id].cap_cal, cal_ctx.pt[cur_item->id].div_cal, cal_ctx.pt[cur_item->id].k);
    //                 uartS (str);

    //                 cur_item = cur_item->sibble;
    //                 seg_updata (cur_item->content);

    //                 sta = 0;
    //                 x_seg_flash_all (0);


    //                 // 准备恢复校准过程中的滤波强度。
    //                 // flag_para_update_fir= 'X';
    //             } else {
    //                 // 保存失败，恢复旧值（断电完蛋）。
    //                 cal_restore_from_pt (&cal_ctx_bk);

    //                 seg_flash_all (0);
    //                 seg_updata ("Err00F");

    //                 sprintf (str, "\n$err load %d", cur_item->id);
    //                 uartS (str);
    //                 osDelay (1000);
    //             }
    //         } else if (key == KEY_K1_ESC) {
    //             sta = 0;
    //             disp_mv = 0;

    //             x_seg_flash_all (0);
    //             seg_updata (cur_item->content);
    //             x_seg_set_point (5 - para_xxx.pt_pos_idx, 0);

    //             // 准备恢复校准过程中的滤波强度。
    //             flag_para_update_fir = 'X';
    //         }

    //         break;

    //     case 3:  // 手动输入mv值。
    //         if (key == KEY_K1_ESC) {
    //         } else if (key == KEY_K2_OPT) {
    //             int_menu_inc_one_seg (0);  // x_seg_inc_one(int_menu.pos);
    //             x_seg_set_point (SEG_L_3, 1);
    //         } else if (key == KEY_K3_MODE) {
    //             int_menu_change_pos();
    //             x_seg_set_point (SEG_L_3, 1);
    //         } else if (key == KEY_K4_ENTER) {
    //             uv_man = int_menu.val;  // 实际mv值是输入值+零点mv值。

    //             x_seg_set_point (SEG_L_3, 0);
    //             x_seg_flash_all (0);


    //             // 准备跳到标定，即输入称重值。
    //             //!!int_menu是全局变量。
    //             int_menu.dig = 6;
    //             int_menu.neg = 0;
    //             int_menu.val = 0;
    //             int_menu.pos = SEG_L_1;

    //             sta = 1;
    //             sprintf (str_i, "%06d", int_menu.val);
    //             seg_updata (str_i);

    //             // idx_flash= SEG_L_1;
    //             x_seg_one_flash (SEG_L_1, DISP_X_MODE_FF);
    //             x_seg_set_point (5 - para_xxx.pt_pos_idx, 1);

    //             sta = 2;
    //         }

    //         break;

    //     default:
    //         break;
    //     }
    // } else if (msg.type == MSG_TYPE_MV_WT) {
    //     char str[32];
    //     char str_x[16];

    //     if (disp_mv) {
    //         // 显示mv值。
    //         ad = msg.ad;
    //         // uv= msg.uv;					//显示实际毫伏值。
    //         uv = msg.uv - cal_ctx.pt[0].uv;  // 显示差值=实际毫伏值-零点毫伏值。
    //         uv_man = -999999;

    //         sprintf (str_x, "%06d", uv);
    //         if (0 == stab_arr.mv_stab)
    //             x_disp_now (str_x, 0, LED_STAB_BIT);
    //         else
    //             x_disp_now (str_x, LED_STAB_BIT, LED_STAB_BIT);

    //         x_seg_set_point (SEG_L_3, 1);

    //         sprintf (str, "\nmv:ad=%d, uv=%d, mvs=%d", ad, uv, stab_arr.mv_stab);
    //         uartS (str);
    //     }
    // }
}

///*标定第1点叶子菜单项处理函数。
// 先显示LoAD1，确定后显示"Add ld"约1秒后，显示mv值。再确定，如果此时STAB有效，则进入设置标定1的称重值；无效（不稳）则死等。QQ如何退出死等状态？
// 在显示mv值时，如果STAB指示灯常亮，按Enter键保存当前零点值，并进入下一个菜单项。
//*/
// void leaf_Fn_cal_load1(MSG_U msg)
//{
//	static uint8_t sta= 0;
//	static uint8_t idx_flash= 1;	//有效范围：1~6
//	static uint8_t disp_mv= 0;		//是否显示mv值。
//	static int32_t ad;				//和uV伴随而来的ad值。
//	uint16_t key;
//	char tmp8[8];
//
//
//	if(msg.type == MSG_TYPE_KEY){
//		key= msg.key;
//
//		switch (sta){
//			case 0:
//				if(KEY_K4_ENTER == key){
//					seg_updata("Add ld ");
//					osDelay(1000);
//
//					disp_mv=1;
//					mode_mv_wt= MODE_MV;
//
//					sta= 1;
//
//					//准备调整校准过程中的滤波强度。
//					flag_para_update_fir= 'C';
//				}
//				else if(KEY_K3_MODE == key){
//					//在同级菜单循环切换。
//					cur_item= cur_item->sibble;
//					seg_updata(cur_item->content);
//				}
//				else if(KEY_K1_ESC == key){
//					//返回上一级菜单。
//					cur_item= cur_item->parent;
//					seg_updata(cur_item->content);
//				}
//
//				break;
//
//			case 1:
//				if(key == KEY_K4_ENTER){
//
//					//需要判断当前STAB为1才能保存，否则什么也不做。
//					if(seg_stab_sta()){
//						disp_mv= 0;	//停止动态显示
//
//						//保存当前mv值。
//						seg_copy_to_tmp(tmp8);		//将当前显示值拷贝到tmp8.
//
//						tmp8[0]= ' ';
//						if('C' == tmp8[1])
//							tmp8[1]= ' ';			//去掉第1个数码管的'C'。
//						tmp8[7]='\0';
//						point[1].uv= atoi(tmp8);	//将tmp8恢复为显示的uv值，并保存到point[0].uv。
//
//
//						//
//						seg_updata("000000 ");
//						idx_flash= 1;
//						set_one_seg_flash(idx_flash, SEG_FLASH);
//						set_one_seg_point(6-para_xxx.pt_pos_idx, 1);	//pos_dot为0：个位小数点（也显示出来）；为1：十位小数点...
//
//						sta= 2;
//					}
//				}
//				else if(key == KEY_K1_ESC){
//					sta= 0;
//					disp_mv= 0;
//
//					seg_updata(cur_item->content);
//
//					//滤波强度要恢复。
//					flag_para_update_fir= 'X';
//				}
//
//				break;
//
//			case 2:
//				//按OPT键调整数值，按MODE键移位，按Enter确认并进入同级下一个菜单。
//				if(key == KEY_K2_OPT){
//					inc_one_seg(idx_flash);
//				}
//				else if(key == KEY_K3_MODE){
//					set_one_seg_flash(idx_flash, SEG_LIGHT);
//
//					idx_flash++;
//					if(6< idx_flash)
//						idx_flash= 1;
//					set_one_seg_flash(idx_flash, SEG_FLASH);
//				}
//				else if(key == KEY_K4_ENTER){
//					//保存旧值。
//					POINT pt;
//					pt.load= point[1].load;
//					pt.ad= point[1].ad;
//					pt.used= point[1].used;
//
//					seg_copy_to_tmp(tmp8);		//将当前显示值拷贝到tmp8.
//
//					tmp8[0]= ' ';
//					if('C' == tmp8[1])
//						tmp8[1]= ' ';			//去掉第1个数码管的'C'。
//					tmp8[7]='\0';
//					point[1].load= atoi(tmp8);	//将tmp8恢复为显示的uv值，并保存到point[0].uv。
//					point[1].ad= ad;
//					point[1].used= 1;
//
//
//					if(0 == para_to_flash_2(FLASH_UPDATE_MASK_PARA_XXXX)){
//						uartS("\nsave load 1");
//
//						cur_item= cur_item->sibble;
//						seg_updata(cur_item->content);

//						sta= 0;
//						seg_flash_all(0);
//
//
//						//准备恢复校准过程中的滤波强度。
//						flag_para_update_fir= 'X';
//					}
//					else{
//						point[1].load= pt.load;		//保存失败，恢复旧值（断电完蛋）。
//						point[1].ad= pt.ad;
//						point[1].used= pt.used;
//
//						seg_flash_all(0);
//						seg_updata("Err00F");
//
//						uartS("\nerr save load 1");
//						osDelay(1000);
//					}
//				}
//				else if(key == KEY_K1_ESC){
//					sta= 0;
//					disp_mv= 0;
//
//					seg_updata(cur_item->content);
//
//					//准备恢复校准过程中的滤波强度。
//					flag_para_update_fir= 'X';
//				}
//
//				break;
//
//			default:
//				break;
//		}
//	}
//	else if(msg.type == MSG_TYPE_MV_WT){
//		uartS("\nmv");
//		if(disp_mv){
//			//显示mv值。
//			seg_mv_to_seg(msg.uv, msg.dat[0]);
//			set_one_seg(1,'C',0);
//
//			ad= msg.ad;
//		}
//	}
//}


///*标定第2点叶子菜单项处理函数。
// 先显示LoAD2，确定后显示"Add ld"约1秒后，显示mv值。再确定，如果此时STAB有效，则进入设置标定2的称重值；无效（不稳）则死等。QQ如何退出死等状态？
// 在显示mv值时，如果STAB指示灯常亮，按Enter键保存当前零点值，并进入下一个菜单项。
//*/
// void leaf_Fn_cal_load2(MSG_U msg)
//{
//	static uint8_t sta= 0;
//	static uint8_t idx_flash= 1;	//有效范围：1~6
//	static uint8_t disp_mv= 0;		//是否显示mv值。
//	static int32_t ad;				//和uV伴随而来的ad值。
//	uint16_t key;
//	char tmp8[8];
//
//
//	if(msg.type == MSG_TYPE_KEY){
//		key= msg.key;
//
//		switch (sta){
//			case 0:
//				if(KEY_K4_ENTER == key){
//					seg_updata("Add ld ");
//					osDelay(1000);
//
//					disp_mv=1;
//					mode_mv_wt= MODE_MV;
//
//					sta= 1;
//
//					//准备调整校准过程中的滤波强度。
//					flag_para_update_fir= 'C';
//				}
//				else if(KEY_K3_MODE == key){
//					//在同级菜单循环切换。
//					cur_item= cur_item->sibble;
//					seg_updata(cur_item->content);
//				}
//				else if(KEY_K1_ESC == key){
//					//返回上一级菜单。
//					cur_item= cur_item->parent;
//					seg_updata(cur_item->content);
//				}
//
//				break;
//
//			case 1:
//				if(key == KEY_K4_ENTER){
//
//					//需要判断当前STAB为1才能保存，否则什么也不做。
//					if(seg_stab_sta()){
//						disp_mv= 0;	//停止动态显示
//
//						//保存当前mv值。
//						seg_copy_to_tmp(tmp8);		//将当前显示值拷贝到tmp8.
//
//						tmp8[0]= ' ';
//						if('C' == tmp8[1])
//							tmp8[1]= ' ';			//去掉第1个数码管的'C'。
//						tmp8[7]='\0';
//						point[2].uv= atoi(tmp8);	//将tmp8恢复为显示的uv值，并保存到point[0].uv。
//
//
//						//
//						seg_updata("000000 ");
//						idx_flash= 1;
//						set_one_seg_flash(idx_flash, SEG_FLASH);
//						set_one_seg_point(6-para_xxx.pt_pos_idx, 1);	//pos_dot为0：个位小数点（也显示出来）；为1：十位小数点...
//
//						sta= 2;
//					}
//				}
//				else if(key == KEY_K1_ESC){
//					sta= 0;
//					disp_mv= 0;
//
//					seg_updata(cur_item->content);
//
//					//滤波强度要恢复。
//					flag_para_update_fir= 'X';
//				}
//
//				break;
//
//			case 2:
//				//按OPT键调整数值，按MODE键移位，按Enter确认并进入同级下一个菜单。
//				if(key == KEY_K2_OPT){
//					inc_one_seg(idx_flash);
//				}
//				else if(key == KEY_K3_MODE){
//					set_one_seg_flash(idx_flash, SEG_LIGHT);
//
//					idx_flash++;
//					if(6< idx_flash)
//						idx_flash= 1;
//					set_one_seg_flash(idx_flash, SEG_FLASH);
//				}
//				else if(key == KEY_K4_ENTER){
//					//保存旧值。
//					POINT pt;
//					pt.load= point[2].load;
//					pt.ad= point[2].ad;
//					pt.used= point[2].used;
//
//					seg_copy_to_tmp(tmp8);		//将当前显示值拷贝到tmp8.
//
//					tmp8[0]= ' ';
//					if('C' == tmp8[1])
//						tmp8[1]= ' ';			//去掉第1个数码管的'C'。
//					tmp8[7]='\0';
//					point[2].load= atoi(tmp8);	//将tmp8恢复为显示的uv值，并保存到point[0].uv。
//					point[2].ad= ad;
//					point[2].used= 1;
//
//
//					if(0 == para_to_flash_2(FLASH_UPDATE_MASK_PARA_XXXX)){
//						uartS("\nsave load 2");
//
//						cur_item= cur_item->sibble;
//						seg_updata(cur_item->content);

//						sta= 0;
//						seg_flash_all(0);
//
//						//准备恢复校准过程中的滤波强度。
//						flag_para_update_fir= 'X';
//					}
//					else{
//						point[2].load= pt.load;		//保存失败，恢复旧值（断电完蛋）。
//						point[2].ad= pt.ad;
//						point[2].used= pt.used;
//
//						seg_flash_all(0);
//						seg_updata("Err00F");
//
//						uartS("\nerr save load 2");
//						osDelay(1000);
//					}
//				}
//				else if(key == KEY_K1_ESC){
//					sta= 0;
//					disp_mv= 0;
//
//					seg_updata(cur_item->content);
//
//					//准备恢复校准过程中的滤波强度。
//					flag_para_update_fir= 'X';
//				}
//
//				break;
//
//			default:
//				break;
//		}
//	}
//	else if(msg.type == MSG_TYPE_MV_WT){
//		uartS("\nmv");
//		if(disp_mv){
//			//显示mv值。
//			seg_mv_to_seg(msg.uv, msg.dat[0]);
//			set_one_seg(1,'C',0);
//
//			ad= msg.ad;
//		}
//	}
//}


///*标定第3点叶子菜单项处理函数。
// 先显示LoAD3，确定后显示"Add ld"约1秒后，显示mv值。再确定，如果此时STAB有效，则进入设置标定3的称重值；无效（不稳）则死等。QQ如何退出死等状态？
// 在显示mv值时，如果STAB指示灯常亮，按Enter键保存当前零点值，并进入下一个菜单项。
//*/
// void leaf_Fn_cal_load3(MSG_U msg)
//{
//	static uint8_t sta= 0;
//	static uint8_t idx_flash= 1;	//有效范围：1~6
//	static uint8_t disp_mv= 0;		//是否显示mv值。
//	static int32_t ad;				//和uV伴随而来的ad值。
//	uint16_t key;
//	char tmp8[8];
//
//
//	if(msg.type == MSG_TYPE_KEY){
//		key= msg.key;
//
//		switch (sta){
//			case 0:
//				if(KEY_K4_ENTER == key){
//					seg_updata("Add ld ");
//					osDelay(1000);
//
//					disp_mv=1;
//					mode_mv_wt= MODE_MV;
//
//					sta= 1;
//
//					//准备调整校准过程中的滤波强度。
//					flag_para_update_fir= 'C';
//				}
//				else if(KEY_K3_MODE == key){
//					//在同级菜单循环切换。
//					cur_item= cur_item->sibble;
//					seg_updata(cur_item->content);
//				}
//				else if(KEY_K1_ESC == key){
//					//返回上一级菜单。
//					cur_item= cur_item->parent;
//					seg_updata(cur_item->content);
//				}
//
//				break;
//
//			case 1:
//				if(key == KEY_K4_ENTER){
//
//					//需要判断当前STAB为1才能保存，否则什么也不做。
//					if(seg_stab_sta()){
//
//						disp_mv= 0;	//停止动态显示
//
//						//保存当前mv值。
//						seg_copy_to_tmp(tmp8);		//将当前显示值拷贝到tmp8.
//
//						tmp8[0]= ' ';
//						if('C' == tmp8[1])
//							tmp8[1]= ' ';			//去掉第1个数码管的'C'。
//						tmp8[7]='\0';
//						point[3].uv= atoi(tmp8);	//将tmp8恢复为显示的uv值，并保存到point[0].uv。
//
//
//						//
//						seg_updata("000000 ");
//						idx_flash= 1;
//						set_one_seg_flash(idx_flash, SEG_FLASH);
//						set_one_seg_point(6-para_xxx.pt_pos_idx, 1);	//pos_dot为0：个位小数点（也显示出来）；为1：十位小数点...
//
//						sta= 2;
//					}
//				}
//				else if(key == KEY_K1_ESC){
//					sta= 0;
//					disp_mv= 0;
//
//					seg_updata(cur_item->content);
//
//					//滤波强度要恢复。
//					flag_para_update_fir= 'X';
//				}
//
//				break;
//
//			case 2:
//				//按OPT键调整数值，按MODE键移位，按Enter确认并进入同级下一个菜单。
//				if(key == KEY_K2_OPT){
//					inc_one_seg(idx_flash);
//				}
//				else if(key == KEY_K3_MODE){
//					set_one_seg_flash(idx_flash, SEG_LIGHT);
//
//					idx_flash++;
//					if(6< idx_flash)
//						idx_flash= 1;
//					set_one_seg_flash(idx_flash, SEG_FLASH);
//				}
//				else if(key == KEY_K4_ENTER){
//					//保存旧值。
//					POINT pt;
//					pt.load= point[3].load;
//					pt.ad= point[3].ad;
//					pt.used= point[3].used;
//
//					seg_copy_to_tmp(tmp8);		//将当前显示值拷贝到tmp8.
//
//					tmp8[0]= ' ';
//					if('C' == tmp8[1])
//						tmp8[1]= ' ';			//去掉第1个数码管的'C'。
//					tmp8[7]='\0';
//					point[3].load= atoi(tmp8);	//将tmp8恢复为显示的uv值，并保存到point[0].uv。
//					point[3].ad= ad;
//					point[3].used= 1;
//
//
//					if(0 == para_to_flash_2(FLASH_UPDATE_MASK_PARA_XXXX)){
//						uartS("\nsave load 3");
//
//						cur_item= cur_item->sibble;
//						seg_updata(cur_item->content);

//						sta= 0;
//						seg_flash_all(0);
//
//						//准备恢复校准过程中的滤波强度。
//						flag_para_update_fir= 'X';
//					}
//					else{
//						point[3].load= pt.load;		//保存失败，恢复旧值（断电完蛋）。
//						point[3].ad= pt.ad;
//						point[3].used= pt.used;
//
//						seg_flash_all(0);
//						seg_updata("Err00F");
//
//						uartS("\nerr save load 3");
//						osDelay(1000);
//					}
//				}
//				else if(key == KEY_K1_ESC){
//					sta= 0;
//					disp_mv= 0;
//
//					seg_updata(cur_item->content);
//
//					//准备恢复校准过程中的滤波强度。
//					flag_para_update_fir= 'X';
//				}
//
//				break;
//
//			default:
//				break;
//		}
//	}
//	else if(msg.type == MSG_TYPE_MV_WT){
//		uartS("\nmv");
//		if(disp_mv){
//			//显示mv值。
//			seg_mv_to_seg(msg.uv, msg.dat[0]);
//			set_one_seg(1,'C',0);
//
//			ad= msg.ad;
//		}
//	}
//}


///*标定第4点叶子菜单项处理函数。
// 先显示LoAD4，确定后显示"Add ld"约1秒后，显示mv值。再确定，如果此时STAB有效，则进入设置标定4的称重值；无效（不稳）则死等。QQ如何退出死等状态？
// 在显示mv值时，如果STAB指示灯常亮，按Enter键保存当前零点值，并进入下一个菜单项。
//*/
// void leaf_Fn_cal_load4(MSG_U msg)
//{
//	static uint8_t sta= 0;
//	static uint8_t idx_flash= 1;	//有效范围：1~6
//	static uint8_t disp_mv= 0;		//是否显示mv值。
//	static int32_t ad;				//和uV伴随而来的ad值。
//	uint16_t key;
//	char tmp8[8];
//
//
//	if(msg.type == MSG_TYPE_KEY){
//		key= msg.key;
//
//		switch (sta){
//			case 0:
//				if(KEY_K4_ENTER == key){
//					seg_updata("Add ld ");
//					osDelay(1000);
//
//					disp_mv=1;
//					mode_mv_wt= MODE_MV;
//
//					sta= 1;
//
//					//滤波强度要变强。
//					flag_para_update_fir= 'C';
//				}
//				else if(KEY_K3_MODE == key){
//					//在同级菜单循环切换。
//					cur_item= cur_item->sibble;
//					seg_updata(cur_item->content);
//				}
//				else if(KEY_K1_ESC == key){
//					//返回上一级菜单。
//					cur_item= cur_item->parent;
//					seg_updata(cur_item->content);
//				}
//
//				break;
//
//			case 1:
//				if(key == KEY_K4_ENTER){
//
//					//需要判断当前STAB为1才能保存，否则什么也不做。
//					if(seg_stab_sta()){
//						disp_mv= 0;	//停止动态显示
//
//						//保存当前mv值。
//						seg_copy_to_tmp(tmp8);		//将当前显示值拷贝到tmp8.
//
//						tmp8[0]= ' ';
//						if('C' == tmp8[1])
//							tmp8[1]= ' ';			//去掉第1个数码管的'C'。
//						tmp8[7]='\0';
//						point[4].uv= atoi(tmp8);	//将tmp8恢复为显示的uv值，并保存到point[0].uv。
//
//
//						//
//						seg_updata("000000 ");
//						idx_flash= 1;
//						set_one_seg_flash(idx_flash, SEG_FLASH);
//						set_one_seg_point(6-para_xxx.pt_pos_idx, 1);	//pos_dot为0：个位小数点（也显示出来）；为1：十位小数点...
//
//						sta= 2;
//					}
//				}
//				else if(key == KEY_K1_ESC){
//					sta= 0;
//					disp_mv= 0;
//
//					seg_updata(cur_item->content);
//
//					//滤波强度要恢复。
//					flag_para_update_fir= 'X';
//				}
//
//				break;
//
//			case 2:
//				//按OPT键调整数值，按MODE键移位，按Enter确认并进入同级下一个菜单。
//				if(key == KEY_K2_OPT){
//					inc_one_seg(idx_flash);
//				}
//				else if(key == KEY_K3_MODE){
//					set_one_seg_flash(idx_flash, SEG_LIGHT);
//
//					idx_flash++;
//					if(6< idx_flash)
//						idx_flash= 1;
//					set_one_seg_flash(idx_flash, SEG_FLASH);
//				}
//				else if(key == KEY_K4_ENTER){
//					//保存旧值。
//					POINT pt;
//					pt.load= point[4].load;
//					pt.ad= point[4].ad;
//					pt.used= point[4].used;
//
//					seg_copy_to_tmp(tmp8);		//将当前显示值拷贝到tmp8.
//
//					tmp8[0]= ' ';
//					if('C' == tmp8[1])
//						tmp8[1]= ' ';			//去掉第1个数码管的'C'。
//					tmp8[7]='\0';
//					point[4].load= atoi(tmp8);	//将tmp8恢复为显示的uv值，并保存到point[0].uv。
//					point[4].ad= ad;
//					point[4].used= 1;
//
//
//					if(0 == para_to_flash_2(FLASH_UPDATE_MASK_PARA_XXXX)){
//						uartS("\nsave load 4");
//
//						cur_item= cur_item->sibble;
//						seg_updata(cur_item->content);

//						sta= 0;
//						seg_flash_all(0);
//
//						//准备恢复校准过程中的滤波强度。
//						flag_para_update_fir= 'X';
//					}
//					else{
//						point[4].load= pt.load;		//保存失败，恢复旧值（断电完蛋）。
//						point[4].ad= pt.ad;
//						point[4].used= pt.used;
//
//						seg_flash_all(0);
//						seg_updata("Err00F");
//
//						uartS("\nerr save load 4");
//						osDelay(1000);
//					}
//				}
//				else if(key == KEY_K1_ESC){
//					sta= 0;
//					disp_mv= 0;
//
//					seg_updata(cur_item->content);
//
//					//滤波强度要恢复。
//					flag_para_update_fir= 'X';
//				}
//
//				break;
//
//			default:
//				break;
//		}
//	}
//	else if(msg.type == MSG_TYPE_MV_WT){
//		uartS("\nmv");
//		if(disp_mv){
//			//显示mv值。
//			seg_mv_to_seg(msg.uv, msg.dat[0]);
//			set_one_seg(1,'C',0);
//
//			ad= msg.ad;
//		}
//	}
//}




///*读取过程中，显示"---"，读取完成并CRC8校验成功，显示"     o"。
//*/
// void leaf_Fn_cal_RdP(MSG_U msg)
//{
//	static uint8_t sta= 0;
//	char tmp8[8];
//	static uint8_t val_idx;
//	uint16_t key;
//	static uint8_t val_change;
//	uint8_t idx_max;	//每次计算，也是合适。
//	char str[64];
//
//
//	if(msg.type != MSG_TYPE_KEY)
//		return;
//
//	key= msg.key;
//
//	switch (sta){
//		case 0:
//			if(KEY_K4_ENTER == key){
//				seg_updata("   ---");
//
//				//读取传感器侧的单线芯片。
//				POINT pt_tmp[7];
//				POINT pt_bk[5];
//				int32_t cp_tmp;	//临时最大量程
//				uint16_t cp_H, cp_L;
//
//				uint8_t crc8;
//				uint8_t u8array[16*5+4];
//				//将ds28的页0 -->pt_tmp[0]
//				ds28_rd_page(0, (uint8_t*)&pt_tmp[0]);	//无POINT.cp。
//				ds28_rd_page(1, (uint8_t*)&pt_tmp[1]);	//无POINT.cp。
//				ds28_rd_page(2, (uint8_t*)&pt_tmp[2]);	//无POINT.cp。
//				ds28_rd_page(3, (uint8_t*)&pt_tmp[3]);	//无POINT.cp。
//				ds28_rd_page(4, (uint8_t*)&pt_tmp[4]);	//无POINT.cp。
//
//				ds28_rd_page(5, (uint8_t*)&pt_tmp[5]);	//最大量程
//				ds28_rd_page(6, (uint8_t*)&pt_tmp[6]);	//CRC8
//
//				//先计算crc是否正确
//				crc8= (uint8_t)pt_tmp[6].used;
//
//				memcpy(u8array+ 16*0, (uint8_t*)&pt_tmp[0], 16);
//				memcpy(u8array+ 16*1, (uint8_t*)&pt_tmp[1], 16);
//				memcpy(u8array+ 16*2, (uint8_t*)&pt_tmp[2], 16);
//				memcpy(u8array+ 16*3, (uint8_t*)&pt_tmp[3], 16);
//				memcpy(u8array+ 16*4, (uint8_t*)&pt_tmp[4], 16);
//				memcpy(u8array+ 16*5, (uint8_t*)&pt_tmp[5], 4);	//最大量程
//
//				crc8= Crc8_Cal(u8array, 16*5+4);
//
//				if(crc8 != (uint8_t)pt_tmp[6].used){
//					//读取后CRC8不对，可以重试。
//					seg_updata("Err101");
//
//					uartS("err:CRC8");
//
//					break;
//				}
//
//				sprintf(str, "\n cp=%d, crc 0k", pt_tmp[5].used);
//				uartS(str);
//
//				//将pt_tmp[]更新到point[]，保存前先备份（好像没用）。
//				uint8_t kk= 0;
//
//				pt_bk[kk].used= point[kk].used;
//				pt_bk[kk].uv= point[kk].uv;
//				pt_bk[kk].ad= point[kk].ad;
//				pt_bk[kk].load= point[kk].load;
//				point[kk].used= pt_tmp[kk].used;
//				point[kk].uv= pt_tmp[kk].uv;
//				point[kk].ad= pt_tmp[kk].ad;
//				point[kk].load= pt_tmp[kk].load;
//
//				kk++;
//				//k=1
//				pt_bk[kk].used= point[kk].used;
//				pt_bk[kk].uv= point[kk].uv;
//				pt_bk[kk].ad= point[kk].ad;
//				pt_bk[kk].load= point[kk].load;
//				point[kk].used= pt_tmp[kk].used;
//				point[kk].uv= pt_tmp[kk].uv;
//				point[kk].ad= pt_tmp[kk].ad;
//				point[kk].load= pt_tmp[kk].load;
//
//				kk++;
//				//k=2
//				pt_bk[kk].used= point[kk].used;
//				pt_bk[kk].uv= point[kk].uv;
//				pt_bk[kk].ad= point[kk].ad;
//				pt_bk[kk].load= point[kk].load;
//				point[kk].used= pt_tmp[kk].used;
//				point[kk].uv= pt_tmp[kk].uv;
//				point[kk].ad= pt_tmp[kk].ad;
//				point[kk].load= pt_tmp[kk].load;
//
//				kk++;
//				//k=3
//				pt_bk[kk].used= point[kk].used;
//				pt_bk[kk].uv= point[kk].uv;
//				pt_bk[kk].ad= point[kk].ad;
//				pt_bk[kk].load= point[kk].load;
//				point[kk].used= pt_tmp[kk].used;
//				point[kk].uv= pt_tmp[kk].uv;
//				point[kk].ad= pt_tmp[kk].ad;
//				point[kk].load= pt_tmp[kk].load;
//
//				kk++;
//				//k=4
//				pt_bk[kk].used= point[kk].used;
//				pt_bk[kk].uv= point[kk].uv;
//				pt_bk[kk].ad= point[kk].ad;
//				pt_bk[kk].load= point[kk].load;
//				point[kk].used= pt_tmp[kk].used;
//				point[kk].uv= pt_tmp[kk].uv;
//				point[kk].ad= pt_tmp[kk].ad;
//				point[kk].load= pt_tmp[kk].load;
//
//
//				cp_H= para_xxx.fs_max_hi;
//				cp_L= para_xxx.fs_max_lo;
//				para_xxx.fs_max_hi= pt_tmp[5].used>> 16;
//				para_xxx.fs_max_lo= pt_tmp[5].used;
//
//
//				if(0 == para_to_flash_2(FLASH_UPDATE_MASK_PARA_XXXX)){
//					uartS("\nsave point[] and CP from DS28 to para_xxx");
//
//					seg_updata("     o");
//					osDelay(1000);
//
//
//					cur_item= cur_item->sibble;
//					seg_updata(cur_item->content);

//					sta= 0;
//					seg_flash_all(0);
//				}
//				else{
//					//编程失败，恢复之前值到RAM（QQ!!但未将这些值写入FLASH）。
//					para_xxx.fs_max_hi= cp_H;
//					para_xxx.fs_max_lo= cp_L;
//
//					uint8_t kk= 0;
//
//					point[kk].used= pt_bk[kk].used;
//					point[kk].uv= pt_bk[kk].uv;
//					point[kk].ad= pt_bk[kk].ad;
//					point[kk].load= pt_bk[kk].load;
//
//					kk++;
//					point[kk].used= pt_bk[kk].used;
//					point[kk].uv= pt_bk[kk].uv;
//					point[kk].ad= pt_bk[kk].ad;
//					point[kk].load= pt_bk[kk].load;
//
//					kk++;
//					point[kk].used= pt_bk[kk].used;
//					point[kk].uv= pt_bk[kk].uv;
//					point[kk].ad= pt_bk[kk].ad;
//					point[kk].load= pt_bk[kk].load;
//
//					kk++;
//					point[kk].used= pt_bk[kk].used;
//					point[kk].uv= pt_bk[kk].uv;
//					point[kk].ad= pt_bk[kk].ad;
//					point[kk].load= pt_bk[kk].load;
//
//
//					seg_flash_all(0);
//					seg_updata("Err00F");
//
//					uartS("\nerr save point[] and CP from DS28");
//
//					osDelay(1000);
//				}
//			}
//			else if(KEY_K2_OPT == key){
//				//在同级菜单循环切换。
//				cur_item= cur_item->up_sibble;
//				seg_updata(cur_item->content);
//			}
//			else if(KEY_K3_MODE == key){
//				//在同级菜单循环切换。
//				cur_item= cur_item->sibble;
//				seg_updata(cur_item->content);
//			}
//			else if(KEY_K1_ESC == key){
//				uartS("\nesc unit");
//
//				cur_item= cur_item->parent;
//				seg_updata(cur_item->content);
//			}
//
//			break;
//
//		default:
//			break;
//	}
//}


///*保存过程中，显示"---"，保存成功，显示"     o"，保存失败显示“Err100"。
// 保存过程，是将仪表已标定值point[5]写入单线芯片，并增加CRC8。
// 保存后，不必回读验证，因为写入函数已有回读（且回读是DS28E写入流程的一步）。
//*/
// void leaf_Fn_cal_SaP(MSG_U msg)
//{
//	static uint8_t sta= 0;
//	char tmp8[8];
//	static uint8_t val_idx;
//	uint16_t key;
//	static uint8_t val_change;
//	uint8_t idx_max;	//每次计算，也是合适。
//	char str[48];
//
//
//	if(msg.type != MSG_TYPE_KEY)
//		return;
//
//	key= msg.key;
//
//	switch (sta){
//		case 0:
//			if(KEY_K4_ENTER == key){
//				seg_updata("   ---");
//
//				//读取传感器侧的单线芯片。
//				POINT pt_tmp;			//用于写入最大量程和CRC8。
//				POINT pt_readback;			//用于写入最大量程和CRC8。
//				uint32_t cp_tmp;		//临时最大量程
//				uint16_t cp_H, cp_L;
//
//				uint8_t crc8;
//				uint8_t u8array[16*5+4];
//
//				pt_tmp.used= para_xxx.fs_max_hi;
//				pt_tmp.used<<= 16;
//				pt_tmp.used|= para_xxx.fs_max_lo;
//				pt_tmp.ad= 0x0;
//				pt_tmp.uv= 0x0;
//				pt_tmp.load= 0x0;
//
////				//赋值（假的值）
////				point[0].used= 1;
////				point[0].ad= 0x123458;
////				point[0].uv= 0x123457;
////				point[0].load= 0x123459;
////
////				point[1].used= 1;
////				point[1].ad= 0x11ABCB;
////				point[1].uv= 0x11ABCC;
////				point[1].load= 0x11ABCD;
////
////				point[2].used= 1;
////				point[2].ad= 0x12ABCB;
////				point[2].uv= 0x12ABCC;
////				point[2].load= 0x12ABCD;
////
////				point[3].used= 0;
////				point[3].ad= 0x13ABCB;
////				point[3].uv= 0x13ABCC;
////				point[3].load= 0x13ABCD;
////
////				point[4].used= 0;
////				point[4].ad= 0x14ABCB;
////				point[4].uv= 0x14ABCC;
////				point[4].load= 0x14ABCD;
//
//
//				memcpy(u8array+ 16*0, (uint8_t*)&point[0], 16);
//				memcpy(u8array+ 16*1, (uint8_t*)&point[1], 16);
//				memcpy(u8array+ 16*2, (uint8_t*)&point[2], 16);
//				memcpy(u8array+ 16*3, (uint8_t*)&point[3], 16);
//				memcpy(u8array+ 16*4, (uint8_t*)&point[4], 16);
//				memcpy(u8array+ 16*5, (uint8_t*)&pt_tmp, 4);	//最大量程
//
//				crc8= Crc8_Cal(u8array, 16*5+4);				//得到CRC8。
//
//				//将ds28的页0 -->pt_tmp[0]
//				if(0 != ds28_wr_page(0, (uint8_t*)&point[0])){	//无POINT.cp。
//					uartS("err:wr DS28 p0");
//					break;
//				}
//				uartS("\nwr DS28 p0 ok");
//
//
//				if(0 != ds28_wr_page(1, (uint8_t*)&point[1])){	//无POINT.cp。
//					uartS("err:wr DS28 p1");
//					break;
//				}
//				uartS("\nwr DS28 p1 ok");
//
//				if(0 != ds28_wr_page(2, (uint8_t*)&point[2])){	//无POINT.cp。
//					uartS("err:wr DS28 p2");
//					break;
//				}
//				uartS("\nwr DS28 p2 ok");
//
//				if(0 != ds28_wr_page(3, (uint8_t*)&point[3])){	//无POINT.cp。
//					uartS("err:wr DS28 p3");
//					break;
//				}
//				uartS("\nwr DS28 p3 ok");
//
//				if(0 != ds28_wr_page(4, (uint8_t*)&point[4])){	//无POINT.cp。
//					uartS("err:wr DS28 p4");
//					break;
//				}
//				uartS("\nwr DS28 p4 ok");
//
//
//
//				if(0 != ds28_wr_page(5, (uint8_t*)&pt_tmp)){	//最大量程
//					uartS("err:wr DS28 cp");
//					break;
//				}
//				uartS("\nwr DS28 p5 ok");
//
//				pt_tmp.used= crc8;
//				if(0 != ds28_wr_page(6, (uint8_t*)&pt_tmp) ){	//CRC8
//					uartS("err:wr DS28 crc8");
//					break;
//				}
//				uartS("\nwr DS28 p6 ok");
//
//				ds28_rd_page(0, (uint8_t*)&pt_readback);
//				sprintf(str, "\npt0: %08x-%08x-%08x-%08x", pt_readback.used, pt_readback.ad, pt_readback.uv, pt_readback.load);
//				uartS(str);
//
//				ds28_rd_page(1, (uint8_t*)&pt_readback);
//				sprintf(str, "\npt1: %08x-%08x-%08x-%08x", pt_readback.used, pt_readback.ad, pt_readback.uv, pt_readback.load);
//				uartS(str);
//
//				ds28_rd_page(2, (uint8_t*)&pt_readback);
//				sprintf(str, "\npt2: %08x-%08x-%08x-%08x", pt_readback.used, pt_readback.ad, pt_readback.uv, pt_readback.load);
//				uartS(str);
//
//				ds28_rd_page(3, (uint8_t*)&pt_readback);
//				sprintf(str, "\npt3: %08x-%08x-%08x-%08x", pt_readback.used, pt_readback.ad, pt_readback.uv, pt_readback.load);
//				uartS(str);
//
//				ds28_rd_page(4, (uint8_t*)&pt_readback);
//				sprintf(str, "\npt4: %08x-%08x-%08x-%08x", pt_readback.used, pt_readback.ad, pt_readback.uv, pt_readback.load);
//				uartS(str);
//
//				ds28_rd_page(5, (uint8_t*)&pt_readback);
//				sprintf(str, "\npt5: %08x-%08x-%08x-%08x", pt_readback.used, pt_readback.ad, pt_readback.uv, pt_readback.load);
//				uartS(str);
//
//				ds28_rd_page(6, (uint8_t*)&pt_readback);
//				sprintf(str, "\npt6: %08x-%02x", pt_readback.used, (uint8_t)pt_readback.used);
//				uartS(str);
//
//
//				//保存成功，显示"    o"。
//				seg_updata("     o");
//			}
//			else if(KEY_K2_OPT == key){
//				//在同级菜单循环切换。
//				cur_item= cur_item->up_sibble;
//				seg_updata(cur_item->content);
//			}
//			else if(KEY_K3_MODE == key){
//				//在同级菜单循环切换。
//				cur_item= cur_item->sibble;
//				seg_updata(cur_item->content);
//			}
//			else if(KEY_K1_ESC == key){
//				uartS("\nesc SA P  ");
//
//				cur_item= cur_item->parent;
//				seg_updata(cur_item->content);
//			}
//
//			break;
//
//		default:
//			break;
//	}
//}


/*将输入的mV值转换为adc采样值。
输入参数 uv: mV值（uv值为整数值，mV值为m.nnn格式）
返回值：对应的adc值（可能为负数）
说明：按激励电压为5.000V，增益为x64计算，即39.0625mV --> 7FFFFF，再简化为39063 --> 7FFFFF。
   uv       39063
------- =  ---------
   ret      0x7FFFFF
*/
static int32_t mv_to_ad (int32_t uv) {
    int64_t ret;

    ret = uv;            // 不能写为：ret= uv*0x7FFFFF; 要先转换，再计算。
    ret *= 0x7FFFFF;
    ret /= (39063 / 2);  // Vref/2

    return (int32_t)ret;
}

/*
 */
void offset_clear (void) {
#ifdef OFFSET_DIV_N_F
    offset_div_n_f_man = 0;     // 手动触发
    offset_div_n_f_pwr_on = 0;  // 上电自动清零
    offset_div_n_f_trace = 0;   // 零点跟踪
    offset_div_n_f_tare = 0;    // 去皮操作
#else
    offset_man = 0;
    offset_pwr_on = 0;
    offset_trace = 0;
#endif
}

// /*开门狗复位。
// 开启看门狗，设置一个很短时间。之后再死等待。
// IWDG->PR默认为0，即divider /4。
// Free-running downcouter.
// Clocked from an independent RC oscillator(can operate in Standy and Stop modes):32K LSI.参见6.2.6。
// Conditional Reset: when the downcounter value becomes less than 0x000。
// QQ调试时发现，执行IWDG->KR=0xCCCC，会让RCC_CSR->LSIRDY为1，而RCC_CSR->LSION为0。奇怪QQ
// */
// __NO_RETURN void wdg_rst (void) {
//     // 实测，不需主动使能LSI，因为在使能IWDG（写入0xCCCC）时，LSIRDY置1。
//     RCC->CSR |= RCC_CSR_LSION;
//     while (0 == (RCC->CSR & RCC_CSR_LSIRDY))
//         ;


//     __disable_irq();    // 写上吧。

//     IWDG->KR = 0xCCCC;  // 使能IWDG。
//     IWDG->KR = 0x5555;  // 使能访问IWDG寄存器。

//     IWDG->RLR = 0x0001;
//     // while(IWDG->SR &0x0002);	//瞎JB写：不需要等待SR，因为对_RLR赋值，不会直接更新。
//     /*These bits are write access protected see Section 30.3.6. They are written by software to
//                                   define the value to be loaded in the watchdog counter each time the value 0xAAAA is written in
//   the IWDG_KR register.*/

//     IWDG->KR = 0xAAAA;  // 喂狗会重载RLR，以加速复位。
//     while (1)
//         ;
// }

///*用到的内置外设（TODO需要重写）：
// USART1，USART3，LPUART1，
// SPI2， SPI3，
// TIM2，TIM7，
// SYSTICK。
//*/
// void core_rst(void)
//{
//	__disable_irq();
//
//	//复位已使用的设备，通过时钟复位。
//	RCC->APB1RSTR1|= RCC_APB1RSTR1_TIM2RST;
//	RCC->APB1RSTR1|= RCC_APB1RSTR1_TIM7RST;
//	RCC->APB1RSTR1|= RCC_APB1RSTR1_SPI2RST;
//	RCC->APB1RSTR1|= RCC_APB1RSTR1_SPI3RST;
//
//	RCC->APB2RSTR|= RCC_APB2RSTR_USART1RST;
//	RCC->APB1RSTR1|= RCC_APB1RSTR2_LPUART1RST;		//RCC_APB1RSTR2_LPUART1;
//	RCC->APB1RSTR1|= RCC_APB1RSTR1_USART3RST;
//
//	//Systick关中断
//	NVIC_DisableIRQ(SysTick_IRQn);
//
//	//__NVIC_SystemReset();
//
//	while(1);
// }

/*用到的内置外设：
USART1，USART3，LPUART1，
SPI2， SPI3，
TIM2，TIM7，
SYSTICK。
*/
// __NO_RETURN void core_rst (void) {
//     __disable_irq();

//     // 复位已使用的设备，通过时钟复位。
//     RCC->APB1RSTR1 |= RCC_APB1RSTR1_TIM2RST;
//     RCC->APB1RSTR1 |= RCC_APB1RSTR1_TIM7RST;
//     RCC->APB1RSTR1 |= RCC_APB1RSTR1_SPI2RST;
//     RCC->APB1RSTR1 |= RCC_APB1RSTR1_SPI3RST;

//     RCC->APB2RSTR |= RCC_APB2RSTR_USART1RST;
//     RCC->APB1RSTR1 |= RCC_APB1RSTR2_LPUART1RST;  // RCC_APB1RSTR2_LPUART1;
//     RCC->APB1RSTR1 |= RCC_APB1RSTR1_USART3RST;

//     // Systick关中断
//     NVIC_DisableIRQ (SysTick_IRQn);

//     //__NVIC_SystemReset();	//实测也会复位GPIO。
//     __DSB(); /* Ensure all outstanding memory accesses included
//                                                                       buffered write are completed before reset */
//     SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
//                             (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
//                             SCB_AIRCR_VECTRESET_Msk); /* Keep priority group unchanged */
//     __DSB();                                          /* Ensure completion of memory access */

//     for (;;)                                          /* wait until reset */
//     {
//         __NOP();
//     }

//     while (1)
//         ;
// }



/************************ END OF FILE ************************/
